1 module collie.codec.http.headers;
2 
3 import collie.utils.string;
4 import kiss.container.Vector;
5 import core.stdc.string;
6 import std.string;
7 import std.array;
8 
9 public import collie.codec.http.headers.httpcommonheaders;
10 public import collie.codec.http.headers.httpmethod;
11 
12 alias HTTPHeaders = HttpHeaders;
13 
14 struct HttpHeaders
15 {
16 	enum kInitialVectorReserve = 32;
17 	
18 	/**
19    * Remove all instances of the given header, returning true if anything was
20    * removed and false if this header didn't exist in our set.
21    */
22 	bool remove(string name){
23 		HTTPHeaderCode code = headersHash(name);
24 		if(code != HTTPHeaderCode.OTHER)
25 			return remove(code);
26 		bool removed = false;
27 		for(size_t i = 0; i < _headersNames.length; ++i){
28 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
29 			if(isSameIngnoreLowUp(name,_headersNames[i])){
30 				_codes[i] = HTTPHeaderCode.NONE;
31 				_headersNames[i] = null;
32 				_headerValues[i] = null;
33 				_deletedCount ++;
34 				removed = true;
35 			}
36 		}
37 		return removed;
38 	}
39 
40 	bool remove(HTTPHeaderCode code){
41 		bool removed = false;
42 		HTTPHeaderCode[] codes = _codes;
43 		HTTPHeaderCode * ptr = codes.ptr;
44 		const size_t len = codes.length;
45 		while(true)
46 		{
47 			size_t tlen = len - (ptr - codes.ptr);
48 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
49 			if(ptr is null)
50 				break;
51 			tlen = ptr - codes.ptr;
52 			ptr ++;
53 			_codes[tlen] = HTTPHeaderCode.NONE;
54 			_headersNames[tlen] = null;
55 			_headerValues[tlen] = null;
56 			_deletedCount ++;
57 			removed = true;
58 		}
59 		return removed;
60 	}
61 
62 	void add(string name, string value)
63 	in{
64 		assert(name.length > 0);
65 	}
66 	body{
67 		HTTPHeaderCode code = headersHash(name);
68 		_codes ~= (code);
69 		_headersNames ~= ((code == HTTPHeaderCode.OTHER) ? name : HTTPHeaderCodeName[code]);
70 		_headerValues~= value;
71 
72 	}
73 
74 	void add(HTTPHeaderCode code, string value)
75 	{
76 		if(code == HTTPHeaderCode.OTHER || code > HTTPHeaderCode.SEC_WEBSOCKET_ACCEPT)
77 			return;
78 		_codes ~= code;
79 		_headersNames ~= HTTPHeaderCodeName[code];
80 		_headerValues~= value;
81 	}
82 
83 	void set(string name,string value)
84 	{
85 		remove(name);
86 		add(name, value);
87 	}
88 
89 	void set(HTTPHeaderCode code, string value)
90 	{
91 		remove(code);
92 		add(code, value);
93 	}
94 
95 	bool exists(string name)
96 	{
97 		HTTPHeaderCode code = headersHash(name);
98 		if(code != HTTPHeaderCode.OTHER)
99 			return exists(code);
100 		for(size_t i = 0; i < _headersNames.length; ++i){
101 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
102 			if(isSameIngnoreLowUp(name,_headersNames[i])){
103 				return true;
104 			}
105 		}
106 		return false;
107 	}
108 
109 	bool exists(HTTPHeaderCode code)
110 	{
111 		HTTPHeaderCode[] codes = _codes;
112 		return memchr(codes.ptr,code,codes.length) != null;
113 	}
114 
115 	void removeAll()
116 	{
117 		_codes = (HTTPHeaderCode[]).init;
118 		_headersNames = (string[]).init;
119 		_headerValues = (string[]).init;
120 		_deletedCount = 0;
121 	}
122 
123 	int opApply(scope int delegate(string name,string value) opeartions)
124 	{
125 		int result = 0;
126 		for(size_t i = 0; i < _headersNames.length; ++i)
127 		{
128 			result = opeartions(_headersNames[i], _headerValues[i]);
129 			if(result)
130 				break;
131 		}
132 		return result;
133 	}
134 
135 	int opApply(scope int delegate(HTTPHeaderCode code,string name,string value) opeartions)
136 	{
137 		int result = 0;
138 		for(size_t i = 0; i < _headersNames.length; ++i)
139 		{
140 			result = opeartions(_codes[i],_headersNames[i], _headerValues[i]);
141 			if(result)
142 				break;
143 		}
144 		return result;
145 	}
146 
147 	HTTPHeaders dub()
148 	{
149 		HTTPHeaders header;
150 		copyTo(header);
151 		return header;
152 	}
153 
154 	void copyTo(ref HTTPHeaders header)
155 	{
156 		foreach(code,name,value; this)
157 		{
158 			if(code == HTTPHeaderCode.NONE) continue;
159 			if(code == HTTPHeaderCode.OTHER)
160 				header.add(name,value);
161 			else
162 				header.add(code,value);
163 		}
164 	}
165 	/**
166    * Get the total number of headers.
167    */
168 	size_t size() const{
169 		return _codes.length - _deletedCount;
170 	}
171 	/**
172    * combine all the value for this header into a string
173    */
174 	string combine(string separator = ", ")
175 	{
176 		Appender!string data = appender!string();
177 		bool frist = true;
178 		foreach(code,name,value; this)
179 		{
180 			if(code == HTTPHeaderCode.NONE) continue;
181 			if(frist) {
182 				data.put(value);
183 				frist = false;
184 			} else {
185 				data.put(separator);
186 				data.put(value);
187 			}
188 		}
189 		return data.data;
190 	}
191 
192 	size_t getNumberOfValues(string name)
193 	{
194 		HTTPHeaderCode code = headersHash(name);
195 		if(code != HTTPHeaderCode.OTHER)
196 			return remove(code);
197 		size_t index = 0;
198 		for(size_t i = 0; i < _headersNames.length; ++i){
199 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
200 			if(isSameIngnoreLowUp(name,_headersNames[i])){
201 				++index;
202 			}
203 		}
204 		return index;
205 	}
206 
207 	size_t getNumberOfValues(HTTPHeaderCode code)
208 	{
209 		size_t index = 0;
210 		HTTPHeaderCode[] codes = _codes;
211 		HTTPHeaderCode * ptr = codes.ptr;
212 		const size_t len = codes.length;
213 		while(true)
214 		{
215 			size_t tlen = len - (ptr - codes.ptr);
216 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
217 			if(ptr is null)
218 				break;
219 			ptr ++;
220 			++ index;
221 		}
222 		return index;
223 	}
224 
225 	string getSingleOrEmpty(string  name)  {
226 		HTTPHeaderCode code = headersHash(name);
227 		if(code != HTTPHeaderCode.OTHER)
228 			return getSingleOrEmpty(code);
229 		for(size_t i = 0; i < _headersNames.length; ++i){
230 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
231 			if(isSameIngnoreLowUp(name,_headersNames[i])){
232 				return _headerValues[i];
233 			}
234 		}
235 		return string.init;
236 	}
237 
238 	string getSingleOrEmpty(HTTPHeaderCode code)  {
239 		HTTPHeaderCode[] codes = _codes;
240 		HTTPHeaderCode * ptr = cast(HTTPHeaderCode *)memchr(codes.ptr,code,codes.length);
241 		if(ptr !is null){
242 			size_t index = ptr - codes.ptr;
243 			return _headerValues[index];
244 		}
245 		return string.init;
246 	}
247 
248 	string[] getValuesByKey(string name)
249 	{
250 		HTTPHeaderCode code = headersHash(name);
251 		if(code != HTTPHeaderCode.OTHER)
252 		{
253 			remove(code);
254 			return null;
255 		}
256 
257 		string[] r = null;
258 		for(size_t i = 0; i < _headersNames.length; ++i){
259 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
260 
261 			if(isSameIngnoreLowUp(name,_headersNames[i])){
262 				r ~= _headerValues[i];
263 			}
264 		}
265 		return r;
266 	}
267 
268 	/**
269    * Process the ordered list of values for the given header name:
270    * for each value, the function/functor/lambda-expression given as the second
271    * parameter will be executed. It should take one const string & parameter
272    * and return bool (false to keep processing, true to stop it). Example use:
273    *     hdrs.forEachValueOfHeader("someheader", [&] (const string& val) {
274    *       std::cout << val;
275    *       return false;
276    *     });
277    * This method returns true if processing was stopped (by func returning
278    * true), and false otherwise.
279    */
280 	alias LAMBDA = bool delegate(string value);
281 	bool forEachValueOfHeader(string name,scope LAMBDA func)
282 	{
283 		HTTPHeaderCode code = headersHash(name);
284 		if(code != HTTPHeaderCode.OTHER)
285 			return forEachValueOfHeader(code,func);
286 		size_t index = 0;
287 		for(size_t i = 0; i < _headersNames.length; ++i){
288 			if(_codes[i] != HTTPHeaderCode.OTHER) continue;
289 			if(isSameIngnoreLowUp(name,_headersNames[i])){
290 				if(func(_headerValues[i]))
291 					return true;
292 			}
293 		}
294 		return false;
295 	}
296 
297 	bool forEachValueOfHeader(HTTPHeaderCode code,scope LAMBDA func)
298 	{
299 		size_t index = 0;
300 		HTTPHeaderCode[] codes = _codes;
301 		HTTPHeaderCode * ptr = codes.ptr;
302 		const size_t len = codes.length;
303 		while(true)
304 		{
305 			size_t tlen = len - (ptr - codes.ptr);
306 			ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen);
307 			if(ptr is null)
308 				break;
309 			tlen = ptr - codes.ptr;
310 			ptr ++;
311 			if(func(_headerValues[tlen]))
312 				return true;
313 		}
314 		return false;
315 	}
316 private:
317 	HTTPHeaderCode[]  _codes ;// = Vector!(HTTPHeaderCode)(2);
318 	string[] _headersNames ;
319 	string[] _headerValues ;
320 	size_t _deletedCount = 0;
321 }